using System;
using System.Collections.Generic;

using Gtk;

using Gazebo.Core;

namespace Gazebo
{
    public partial class PreferencesDialog : Gtk.Dialog
    {
        private TreeStore preferencesTreeStore = new TreeStore(typeof(TreeNode));
        
        private TreeModelFilter preferencesTreeStoreFilter;
        
        private TreeIter rootIter;
        
        private List<IPreferencePane> panes = new List<IPreferencePane>();
        
        public PreferencesDialog()
        {
            this.Build();
			
            rootIter = preferencesTreeStore.AppendNode();
            BuildTree();
            
            preferencesTreeStoreFilter = new TreeModelFilter(preferencesTreeStore,
                                                             preferencesTreeStore.GetPath(rootIter));
            
            preferencesTree.AppendColumn("Name", new CellRendererText(),
                                         new TreeCellDataFunc(TreeNodeDataFunc));
            
            preferencesTree.Model = preferencesTreeStoreFilter;
            preferencesTree.Selection.Changed += OnTreeSelectionChanged;
            preferencesTree.Selection.SelectFunction = ValidateTreeSelection;
            
            preferencesTree.ExpandAll();
        }
        
        private static bool ValidateTreeSelection(TreeSelection selection, TreeModel model,
                                                  TreePath path, bool selected)
        {
            TreeIter iter;
            
            if (!model.GetIter(out iter, path)) {
                return true;
            }
            
            TreeNode node = (TreeNode) model.GetValue(iter, 0);
            return node.Pane != null;
        }

        private void OnTreeSelectionChanged(object sender, EventArgs e)
        {
            TreeIter iter;
            
            if (preferencesPane.Child != null) {
                preferencesPaneVBox.Hide();
                
                preferencesPane.Remove(preferencesPane.Child);
                paneHeader.Text = "";
            }
            
            if (!preferencesTree.Selection.GetSelected(out iter)) {
                return;
            }
            
            TreeNode node = (TreeNode) preferencesTreeStoreFilter.GetValue(iter, 0);
            if (node.Pane != null) {
                paneHeader.Markup = "<b><big>" + node.Label + "</big></b>";
                
                preferencesPane.Add(node.Pane.PaneWidget);
                node.Pane.PaneWidget.Show();
                
                preferencesPaneVBox.Show();
            }
        }
        
        private static void TreeNodeDataFunc(TreeViewColumn col, CellRenderer r,
                                             TreeModel m, TreeIter i) {
            CellRendererText text = (CellRendererText) r;
            TreeNode node = m.GetValue(i, 0) as TreeNode;
            
            if (node == null) {
                text.Text = "";
            } else {
                text.Text = node.Label;
            }
        }
        
        private void BuildTree()
        {
            foreach (IPreferencePane pane in AddinUtilities.CreateExtensionObjects<IPreferencePane>("/Gazebo/PreferencePane")) {
                AddPane(pane);
                panes.Add(pane);
            }
        }
        
        private void AddPane(IPreferencePane pane)
        {
            string[] pieces = pane.Path.Split('/');
            
            TreeIter iter = rootIter;
            TreeIter found;
            int i;
            
            for (i = 0; i < pieces.Length - 1; i++) {
                if (pieces[i] == "") {
                    continue;
                }
                
                if (!HasLabeledChild(iter, pieces[i], out found)) {
                    TreeNode node = new TreeNode();
                    node.Label = pieces[i];
                    
                    iter = preferencesTreeStore.AppendValues(iter, node);
                } else {
                    iter = found;
                }
            }
            
            if (HasLabeledChild(iter, pieces[i], out found)) {
                TreeNode node = (TreeNode) preferencesTreeStore.GetValue(found, 0);
                if (node.Pane == null) {
                    node.Pane = pane;
                }
            } else {
                TreeNode node = new TreeNode();
                node.Label = pieces[i];
                node.Pane = pane;
                
                preferencesTreeStore.AppendValues(iter, node);
            }
        }
        
        private bool HasLabeledChild(TreeIter iter, string label, out TreeIter child)
        {
            TreeIter i;
            
            child = TreeIter.Zero;
            
            if (!preferencesTreeStore.IterNthChild(out i, iter, 0)) {
                return false;
            }
            
            do {
                TreeNode node = (TreeNode) preferencesTreeStore.GetValue(i, 0);
                
                if (node.Label == label) {
                    child = i;
                    return true;
                }
            } while (preferencesTreeStore.IterNext(ref i));
            
            return false;
        }

        protected virtual void OnButtonOkClicked (object sender, System.EventArgs e)
        {
            try {
                lock (PreferencesManager.GlobalManager.WriteLock) {
	                foreach (IPreferencePane pane in panes) {
	                    pane.Apply();
                    }
                    
                    PreferencesManager.GlobalManager.Save();
                }
                
                this.Respond(ResponseType.Ok);
            } catch (InvalidOperationException ex) {
                MessageDialog d = new MessageDialog(this, DialogFlags.Modal, MessageType.Error,
                                                    ButtonsType.Ok, "Failed to validate settings: {0}",
                                                    ex.Message);
                
                try {
                    d.Run();
                } finally {
                    d.Destroy();
                }
            }
        }
        
        private class TreeNode
        {
            public string Label;
            public IPreferencePane Pane;
        }
    }
}
